package com.aspirecn.core.commons;

import javax.servlet.http.HttpServletRequest;

import jodd.cache.Cache;
import jodd.cache.LRUCache;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.log4j.Logger;

import com.aspirecn.core.ShareCommon;
import com.aspirecn.core.object.IP;


public class IPUtils {

	public final static Logger logger = Logger.getLogger(IPUtils.class);
	
	/**
	 * IP地址转换为整型
	 * @param strIp
	 * @return
	 */
	public static long ipToLong(String strIp) {
		long[] ip = new long[4];
		// 先找到IP地址字符串中.的位置
		int position1 = strIp.indexOf(".");
		int position2 = strIp.indexOf(".", position1 + 1);
		int position3 = strIp.indexOf(".", position2 + 1);
		// 将每个.之间的字符串转换成整型
		ip[0] = Long.parseLong(strIp.substring(0, position1));
		ip[1] = Long.parseLong(strIp.substring(position1 + 1, position2));
		ip[2] = Long.parseLong(strIp.substring(position2 + 1, position3));
		ip[3] = Long.parseLong(strIp.substring(position3 + 1));
		return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];
	}

	/**
	 * 将十进制整数形式转换成127.0.0.1形式的ip地址
	 * 
	 * @param longIp
	 * @return
	 */
	public static String longToIP(long longIp) {
		StringBuffer sb = new StringBuffer("");
		// 直接右移24位
		sb.append(String.valueOf((longIp >>> 24)));
		sb.append(".");
		// 将高8位置0，然后右移16位
		sb.append(String.valueOf((longIp & 0x00FFFFFF) >>> 16));
		sb.append(".");
		// 将高16位置0，然后右移8位
		sb.append(String.valueOf((longIp & 0x0000FFFF) >>> 8));
		sb.append(".");
		// 将高24位置0
		sb.append(String.valueOf((longIp & 0x000000FF)));
		return sb.toString();
	}
	
	
	/**
	 * 计算网段范围
	 * 格式： 192.168.1.1
	 *      192.168.1.1-192.168.1.5
	 *      221.179.186.160/27
	 */
    //判断IP是否在网段中，网段格式为：221.179.186.160/27，也可以是单独IP
    public static long[] getIPRange(String rangeip){
    	
    	String formatRangeip = StringUtils.deleteWhitespace( rangeip );
    	
    	long startNum = 0;
    	long endNum = 0;
    	
    	if( StringUtils.contains(formatRangeip, "-") )
    	{
    		startNum = IPUtils.ipToLong(StringUtils.substringBefore(formatRangeip, "-"));
    		endNum = IPUtils.ipToLong(StringUtils.substringAfterLast(formatRangeip, "-"));
    	}
    	else if( StringUtils.contains(formatRangeip, "/") )
    	{
    		int yanma = NumberUtils.toInt( StringUtils.substringAfter(formatRangeip, "/") );			
			int ipcount = 32-yanma;
			double rangeCount = Math.pow(2,ipcount)-2;
			
			startNum = ipToLong(StringUtils.substringBefore(formatRangeip, "/")) + 1;			
			endNum = Math.round(startNum + rangeCount);
    	}
    	else
    	{
    		startNum = IPUtils.ipToLong(formatRangeip);
    		endNum = startNum;
    	}
    	
    	if( startNum > 0 && endNum > 0 )
    	{
    		if( endNum >= startNum )
    			return new long[]{startNum,endNum};
    		else
    			return new long[]{endNum,startNum};
    	}
    	else
    	{
    		return null;
    	}
    }
	
    public final static String ForMMTest = "ForMMTest";
    public final static String XForwardedFor = "X-Forwarded-For";
    public final static String XForwardedForNMPProxy = "X-Forwarded-For-NMPProxy";
    public final static String RemoteAddr = "RemoteAddr";
    public final static String ClientIP = "clientip";
    
    /**
	 * 获取IP地址，不同前端配置不同，这里需要根据IP地址规范进行
	 * @param strIp
	 * @return
	 */
    public static String getIP(HttpServletRequest request) {
		
    	String retIP = null;
    	
    	//如果配置文件中可以使用该参数，则优先获取
    	if( Configuration.getInstance().useForMMTest() )
    	{
    		String forMMTest = HttpUtils.getHeaderIgnoreCase(request,ForMMTest);
    		if( StringUtils.isNotBlank(forMMTest) )
    		{
    			return StringUtils.trimToEmpty(forMMTest);
    		}
    	}
    	
    	//目前支持在三种内容中定位IP
    	//X-Forwarded-For-NMPProxy，getRemoteAddr，X-Forwarded-For
    	//8-26新增 ClientIP
    	String ipExpressions = Configuration.getInstance().getIPExpression();
    	String remoteAddr = request.getRemoteAddr();
    	String xForwardedFor = StringUtils.deleteWhitespace(StringUtils.trimToEmpty(HttpUtils.getHeaderIgnoreCase(request,XForwardedFor)));
    	String xForwardedForNMPProxy = StringUtils.deleteWhitespace(StringUtils.trimToEmpty(HttpUtils.getHeaderIgnoreCase(request,XForwardedForNMPProxy)));
    	String clientIP = StringUtils.deleteWhitespace(StringUtils.trimToEmpty(HttpUtils.getHeaderIgnoreCase(request,ClientIP)));
    	
    	String[] ipExpressionArray = StringUtils.split(ipExpressions, "||");//  ipExpressions.split("||");
    	
    	//System.out.println( ipExpressionArray.length );
    	//System.out.println( ArrayUtils.toString(ipExpressionArray) );
    	for( String ipExpression:ipExpressionArray )
    	{
    		String ip = getTrueIPByExpression( remoteAddr,xForwardedFor,xForwardedForNMPProxy,clientIP,StringUtils.trimToEmpty(ipExpression) );
    		if( StringUtils.isNotBlank(ip) )
    		{
    			retIP = ip;
    			break;
    		}
    	}
    	
    	if( StringUtils.isBlank(retIP) )
    	{
    		retIP = remoteAddr;
    	}
    	
		if (StringUtils.isNotBlank(xForwardedFor))// xForwardedFor有值，说明经过代理，打印日志
		{
			logger.debug(RemoteAddr + "=" + remoteAddr + ";" + XForwardedFor
					+ "=" + xForwardedFor + ";" + XForwardedForNMPProxy + "="
					+ xForwardedForNMPProxy + ";ip=" + retIP);
		}
    	
    	return retIP;
	}
    
    
    /**
     * 根据自定义表达式获取IP
     * [RemoteAddr=221.179.8.110][X-Forwarded-For=221.179.8.110][value=(X-Forwarded-For=221.179.8.110)-1]
     * 含义：RemoteAddr中包含221.179.8.110，X-Forwarded-For中包含221.179.8.110，X-Forwarded-For中221.179.8.110位置前面一个的IP
     * 
     * [RemoteAddr=192.168.1.1][X-Forwarded-For=192.168.1.101][value=(RemoteAddr)]
     * 前面两个是条件
     * 最后一个是选中哪个值
     * 括号中可以是RemoteAddr，X-Forwarded-For-NMPProxy
     * 或者X-Forwarded-For 某一个位置 + 偏移量
     * 
     */
	private static String getTrueIPByExpression(String remoteAddr,
			String xForwardedFor, String xForwardedForNMPProxy, String clientIP,
			String expression) {

		String[] expressions = StringUtils.substringsBetween(expression, "[", "]");
		String retIP = null;
		
		for( String s:expressions )
		{
			//一个单元的name和value
			String name = StringUtils.substringBefore(s, "=");
			String value = StringUtils.substringAfter(s, "=");
			
			//一个条件不合适直接跳出
			if( StringUtils.equalsIgnoreCase(name, RemoteAddr) )
			{
				if( !StringUtils.containsIgnoreCase(remoteAddr, value) )
				{
					return null;
				}
			}
			else if( StringUtils.equalsIgnoreCase(name, XForwardedFor) )
			{
				if( !StringUtils.containsIgnoreCase(xForwardedFor, value) )
				{
					return null;
				}
			}
			else if( StringUtils.equalsIgnoreCase(name, XForwardedForNMPProxy) )
			{
				if( !StringUtils.containsIgnoreCase(xForwardedForNMPProxy, value) )
				{
					return null;
				}
			}
			else if( StringUtils.equalsIgnoreCase(name, "value") )//获取内容的部分
			{
				String[] ips = xForwardedFor.split(",");
				String subexpression = StringUtils.substringBetween(value, "(", ")");
				//位置
				int position = NumberUtils.toInt(StringUtils.remove(StringUtils.substringAfter(value, ")"),"+"));
				if( StringUtils.contains(subexpression, "=") )
				{
					String subName = StringUtils.substringBefore(subexpression, "=");
					String subValue = StringUtils.substringAfter(subexpression, "=");
					
					if( StringUtils.equalsIgnoreCase(subName, XForwardedFor) )
					{
						int tempPosi = ArrayUtils.indexOf(ips, subValue);
						//不包含
						if( tempPosi == -1 )
						{
							return null;
						}
						else
						{
							int readPosition = position + tempPosi;
							if( ips.length > readPosition && readPosition >= 0 )
							{
								retIP = ips[readPosition];
							}
						}
					}
				}
				else
				{
					if( StringUtils.equalsIgnoreCase(subexpression, XForwardedFor) )
					{
						int readPosition = position;
						if( ips.length > readPosition && readPosition >= 0 )
						{
							retIP = ips[readPosition];
						}
					}
					else if( StringUtils.equalsIgnoreCase(subexpression, RemoteAddr) )
					{
						return remoteAddr;
					}
					else if( StringUtils.equalsIgnoreCase(subexpression, ClientIP) )
					{
						return clientIP;
					}
					else if( StringUtils.equalsIgnoreCase(subexpression, XForwardedForNMPProxy) )
					{
						return xForwardedForNMPProxy;
					}
				}
			}
		}
		
		return retIP;
	}	
	
	//等待添加缓存，因为一个页面可能会调用n次
	public static Cache<String,IP> localIPCacheService = new LRUCache<String,IP>(1000);
	private static IP nullIP = null;
	public static IP getIPObject( String ip )
	{		
		//根据网段保存缓存，提升一点命中率
		//String wangduan = getIPWangDuan( ip );
		
		String cachekey = StringUtils.trimToEmpty(ip);
		IP retIP = localIPCacheService.get(cachekey);
		
		//有缓存，但是标记为适配失败，则直接返回空，不再适配
		String nullIPAddress = "-999";
		if( retIP != null )
		{
			if( StringUtils.equals( retIP.getIpAddress(), nullIPAddress ) )//-999代表查询过，失败的
			{
				return null;
			}
		}		
		
		if( retIP == null )
		{
			retIP = ShareCommon.getInstance().getIPRangeCache().search(ip);
			if( retIP != null )
			{
				localIPCacheService.put(cachekey, retIP);
			}
			else
			{
				if( nullIP == null )
				{
					nullIP = new IP();
					nullIP.setIpAddress(nullIPAddress);
				}
				
				localIPCacheService.put(cachekey, nullIP);
			}
		}
		
		return retIP;
	}	
	
//	/**
//	 * 获取IP网段信息
//	 */
//	private static String getIPWangDuan( String ip )
//	{
//		return StringUtils.substringBeforeLast(ip, ".");
//	}	
	
	public static void main( String[] args )
	{
		String remoteAddr = "192.168.1.1";
		
		System.out.println( StringUtils.substringBeforeLast(remoteAddr, ".") );
//		String xForwardedFor = "";
//		String xForwardedForNMPProxy = "10.2.2.3";
//		String expression = "[RemoteAddr=192.168.1.1][value=(X-Forwarded-For)+3]";
//		
//		System.out.println(getTrueIPByExpression(remoteAddr, xForwardedFor,
//				xForwardedForNMPProxy, expression));
		
		System.out.println( ArrayUtils.toString(getIPRange("117.136.5.32/32")) );
		
//		System.out.println( Integer.parseInt("11111111", 2) );
		System.out.println( Math.pow(2,0) );
	}
}
